package com.my.test.transport;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.Iterator;
import java.util.Vector;

import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.ChannelSftp.LsEntry;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpATTRS;
import com.jcraft.jsch.SftpException;
import com.jcraft.jsch.UserInfo;

/**
 * 实现在linux 系统之间下载文件（通过sftp协议）
 * 
 * @author zongbo
 *
 */
public class SftpUtil {

	Session session = null;
	ChannelSftp channel = null;
	SftpFileFilter fileFilter = null;

	private SftpUtil() {
		// TODO Auto-generated constructor stub
	}

	/**
	 * 连接sftp服务器
	 * 
	 * @param host
	 *            远程主机ip地址
	 * @param user
	 *            用户名
	 * @param password
	 *            密码
	 * @return
	 * @throws JSchException
	 */
	public static SftpUtil connect(String host, String user, String password) throws JSchException {
		return connect(host, null, user, password);
	}

	/**
	 * 连接sftp服务器
	 * 
	 * @param host
	 *            远程主机ip地址
	 * @param port
	 *            sftp连接端口，null 时为默认端口
	 * @param user
	 *            用户名
	 * @param password
	 *            密码
	 * @return
	 * @throws JSchException
	 */
	public static SftpUtil connect(String host, Integer port, String user, String password) throws JSchException {
		SftpUtil sftp = new SftpUtil();
		try {
			JSch jsch = new JSch();
			if (port != null) {
				sftp.session = jsch.getSession(user, host, port.intValue());
			} else {
				sftp.session = jsch.getSession(user, host);
			}
			sftp.session.setUserInfo(new UserInfo() {

				public String getPassphrase() {
					return null;
				}

				public String getPassword() {
					return null;
				}

				public boolean promptPassword(String arg0) {
					return false;
				}

				public boolean promptPassphrase(String arg0) {
					return false;
				}

				public boolean promptYesNo(String arg0) {
					return true;// notice here
				}

				public void showMessage(String arg0) {
				}

			});

			sftp.session.setPassword(password);
			// 设置第一次登陆的时候提示，可选值:(ask | yes | no)
			sftp.session.setConfig("StrictHostKeyChecking", "no");
			// 10秒连接超时
			sftp.session.connect(10000);
			// 打开sftp通道
			sftp.openChannel();
		} catch (JSchException e) {
			System.out.println("SftpAccess 获取连接发生错误:");
			e.printStackTrace();
			throw e;
		}
		return sftp;
	}

	/**
	 * 打开sftp通道
	 * 
	 * @return
	 * @throws JSchException
	 */
	private SftpUtil openChannel() throws JSchException {
		try {
			channel = (ChannelSftp) session.openChannel("sftp");
			channel.connect();
		} catch (JSchException e) {
			// TODO Auto-generated catch block
			System.out.println("SftpAccess 打开sftp通道时发生错误:");
			e.printStackTrace();
			throw e;
		}
		return this;
	}

	/**
	 * 下载远程单个文件
	 * 
	 * @param srcFile
	 *            远程服务器文件路径（包括文件名）
	 * @param OutputStream
	 *            输出流
	 */
	public SftpUtil downloadFile(String srcFile, OutputStream os) {
		try {
			channel.get(srcFile, os);
		} catch (SftpException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return this;
	}

	/**
	 * 下载远程单个文件
	 * 
	 * @param srcFile
	 *            远程服务器文件路径（包括文件名）
	 * @param localDirectory
	 *            本地文件目录
	 */
	public SftpUtil downloadFile(String srcFile, String localDirectory) {
		return downloadFile(srcFile, localDirectory, null);
	}

	/**
	 * 下载远程单个文件
	 * 
	 * @param srcFile
	 *            远程服务器文件路径（包括文件名）
	 * @param localDirectory
	 *            本地文件目录
	 * @param newFileName
	 *            本地新文件名,如果为null，则使用源文件名
	 */
	public SftpUtil downloadFile(String srcFile, String localDirectory, String newFileName) {

		// 创建本地文件夹
		File file = new File(localDirectory);
		if (!file.exists()) {
			file.mkdir();
		}

		String fileName = srcFile.substring(srcFile.lastIndexOf(File.separator) + 1, srcFile.length());

		String localFile = "";// 计算文件名
		if (localDirectory.endsWith(File.separator)) {
			localFile = localDirectory;
		} else {
			localFile = localDirectory + File.separator;
		}

		if (newFileName == null || "".equals(newFileName.trim())) {
			localFile += fileName;// 使用源文件名
		} else {
			localFile += newFileName;// 使用自定义文件名
		}
		// System.out.println("dowloading "+ fileName);

		try {
			// 下载文件
			channel.get(srcFile, localFile);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return this;
	}

	/**
	 * sftp下载文件（夹）
	 * 
	 * @param directory
	 *            下载文件上级目录
	 * @param srcFile
	 *            下载文件完全路径
	 * @param saveFile
	 *            保存文件路径
	 * @param sftp
	 *            ChannelSftp
	 * @throws UnsupportedEncodingException
	 */
	@SuppressWarnings("rawtypes")
	public SftpUtil downloadDirectory(String srcDirectory, String localDirectory) {

		try {
			// 列出文件
			Vector conts = channel.ls(srcDirectory);
			System.out.println("file count: " + conts.size() + ", srcDirectory " + srcDirectory);

			// 文件夹(路径)
			for (Iterator iterator = conts.iterator(); iterator.hasNext();) {
				LsEntry obj = (LsEntry) iterator.next();
				String fileName = obj.getFilename();
				// System.out.println("fileName= "+ fileName);
				// linux下的特殊文件夹不处理
				if (fileName.equals(".") || fileName.equals("..")) {
					// System.out.println(" 特殊文件，忽略此文件 fileName= "+ fileName );
					continue;
				}
				// 使用文件过滤器来过滤文件
				if (fileFilter != null && !fileFilter.accept(obj)) {
					// System.out.println(" 忽略此文件 ");
					continue;
				}

				// 获取文件的属性信息
				SftpATTRS attr = obj.getAttrs();

				if (attr.isDir()) {
					// 文件夹
					String tempSrcDirectory = srcDirectory + File.separator + fileName;
					String tempLocalDirectory = localDirectory + File.separator + fileName;
					// 递归
					downloadDirectory(tempSrcDirectory, tempLocalDirectory);
				} else {
					// 文件
					// System.out.println("dowloading "+ fileName);
					String srcFile = srcDirectory + File.separator + fileName;
					String localFile = null;
					if (fileFilter != null) {// 先通过filter获取
						localFile = fileFilter.createFileName(obj, localDirectory, fileName);
					}
					if (localFile == null) {// filter未返回，使用默认值
						localFile = localDirectory + File.separator + fileName;
					}

					String localPath = localFile.substring(0, localFile.lastIndexOf(File.separator));
					// 创建本地文件夹
					File file = new File(localPath);
					if (!file.exists()) {
						file.mkdir();
					}
					channel.get(srcFile, localFile);
				}
			}

		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}

		return this;

	}

	/**
	 * 上传单个文件
	 * 
	 * @param localFile
	 *            要上传的文件（包括文件名）
	 * @param remoteDirectory
	 *            远程文件夹目录
	 * @throws Exception 
	 */
	public SftpUtil uploadFile(String localFile, String remoteDirectory) throws Exception {
		return uploadFile(localFile,remoteDirectory,null) ;
	}
	/**
	 * 上传单个文件
	 * 
	 * @param localFile
	 *            要上传的文件（包括文件名）
	 * @param remoteDirectory
	 *            远程文件夹目录
	 * @param newFileName
	 *            上传后的新文件名,如果为null，则使用源文件名
	 * @throws Exception 
	 */
	public SftpUtil uploadFile(String localFile, String remoteDirectory, String newFileName) throws Exception {
		
		InputStream in = null;
		try {
			File file = new File(localFile);
			//新文件名
			String fileName = (newFileName!=null && newFileName.length() > 0)?newFileName:file.getName() ;
			in = new FileInputStream(file) ;
			//上传文件
			return uploadFile(in,remoteDirectory,fileName);
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
			throw e ;
		} finally {
			try {
				if(in != null) in.close(); 
			} catch (Exception e2) {}
		}
	}

	/**
	 * 上传单个文件，请注意关闭InputStream
	 * 
	 * @param in
	 *            要上传的文件流
	 * @param remoteDirectory
	 *            远程文件夹目录
	 * @param newFileName
	 *            上传后的新文件名
	 * @throws Exception 
	 */
	public SftpUtil uploadFile(InputStream in, String remoteDirectory, String newFileName) throws Exception {
		//创建远程文件夹
		createRemoteDirIfNeed(remoteDirectory) ;
		channel.cd(remoteDirectory);
		
		try {
			//上传文件
			channel.put(in, newFileName);
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
			throw e ;
		} finally {
		}
		
		return this;
	}

	/**
	 * 创建远程机器的文件夹（如果需要的话）
	 * @param dir	远程文件夹
	 * @throws SftpException
	 */
	public void createRemoteDirIfNeed(String remoteDir) throws SftpException {
		try {
			channel.cd(remoteDir);
		} catch (SftpException sException) {
			if (ChannelSftp.SSH_FX_NO_SUCH_FILE == sException.id) {
				channel.mkdir(remoteDir);
			}
		}

	}

	/**
	 * 关闭连接
	 */
	public void close() {

		if (channel != null) {
			channel.disconnect();
		}
		if (session != null) {
			session.disconnect();
		}
	}

	public ChannelSftp getChannel() {
		return channel;
	}

	/**
	 * 设置文件过滤器
	 * 
	 * @param fileFilter
	 * @return
	 */
	public SftpUtil setFileFilter(SftpFileFilter fileFilter) {
		this.fileFilter = fileFilter;
		return this;
	}

	/**
	 * 文件（夹）过滤器
	 * 
	 * @author zongbo
	 *
	 */
	public static interface SftpFileFilter {
		/**
		 * 是否接受此文件（夹） true-接受此文件（夹） false-忽略此文件（夹）
		 * 
		 * @param obj
		 *            有可能是文件，有可能是文件夹
		 * @return
		 */
		public boolean accept(LsEntry obj);

		/**
		 * 生成文件名称，如果想使用系统自动生成的路径，返回null
		 * 
		 * @param obj
		 *            文件对象
		 * @param defaultLocalDirectory
		 *            系统生成的文件目录
		 * @param fileName
		 *            文件名
		 * @return
		 */
		public String createFileName(LsEntry obj, String defaultLocalDirectory, String fileName);
	}

	public static void main(String[] args) {

		try {
			SftpUtil f = SftpUtil.connect("192.168.1.224", "root", "cntmi1234");
			f.uploadFile("/test/2.docx", "/root/").close();
//			Vector conts = f.getChannel().ls("/root/");
//			LsEntry obj = (LsEntry) conts.get(0);
//			System.out.println(obj.getAttrs().getMTime());
			// System.out.println(obj.getAttrs().getATime());
			// System.out.println(System.currentTimeMillis());
			// File file = new File("/test/1.docx");
			// System.out.println(file.lastModified());
			// SftpUtil
			// .connect("192.168.1.224","root", "cntmi1234")
			// .setFileFilter(new SftpFileFilter() {//设置过滤器
			// public boolean accept(LsEntry obj) {
			// //只要jpg文件
			// return obj.getAttrs().isDir() || obj.getFilename().endsWith("jpg");
			// }
			// public String createFileName(LsEntry obj,String defaultLocalDirectory, String
			// defaultFileName) {
			// String barCode = defaultFileName.split("_")[0] ;
			// return "/test/a/"+barCode+"/" + defaultFileName;
			// }
			// })
			// .downloadDirectory("/home/jsoup/update_pic/", "/test/a/")
			// .close()
			// ;
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
